
import Coords from "./Coords";
import type World from "./World";
import rand from "./rand";

export default class Firework {
  readonly world: World;
  protected x: number = 0;
  protected y: number = 0;
  protected dead_x: number = 0;
  protected dead_y: number = 0;

  protected hitX: boolean;
  protected hitY: boolean;
  protected coords: Coords = new Coords();
  protected hue: number;
  protected brightness: number;
  protected alpha: number;
  protected line_width: number;
  protected target_radius: number = 0;
  protected velocity_x: number = 0;
  protected velocity_y: number = 0;
  protected lifetime: number = 0;
  protected begin_speed: number = 0;
  show_target: boolean;

  protected get cur_angle(): number {
    const angle = Math.atan2(this.velocity_y, this.velocity_x);
    return angle + 0.5 * Math.PI;
  };

  protected get cur_speed(): number {
    const qdx = Math.pow(this.velocity_x, 2)
    const qdy = Math.pow(this.velocity_y, 2)
    return Math.sqrt(qdx + qdy)
  }

  constructor(world: World) {
    this.world = world;
    this.hitX = false;
    this.hitY = false;
    this.hue = world.firework_hue;
    this.brightness = rand(50, 80);
    this.alpha = rand(0.8, 1);
    this.line_width = world.line_width;
    this.show_target = this.world.show_target;
  }

  shot(x: number, y: number, dead_x: number, dead_y: number): this {
    this.dead_x = dead_x;
    this.dead_y = dead_y;
    this.coords.init(x, y, 3);
    this.x = x;
    this.y = y;

    const g = this.world.gravity;
    const target_h = y - dead_y
    const max_h = target_h + rand(0, this.world.firework_height_variance);
    this.velocity_y = -Math.sqrt(2 * max_h * g);
    const a = 0.5 * g;
    const b = this.velocity_y;
    const c = target_h;
    const d = b * b - 4 * a * c;
    if (d === 0) {
      this.lifetime = -0.5 * b / a;
    } else if (d > 0) {
      const qd = Math.sqrt(d)
      if (Math.floor(Math.random() * 100) % 2) {
        this.lifetime = 0.5 * (-b - qd) / a
      } else {
        this.lifetime = 0.5 * (-b + qd) / a
      }
    }
    const target_l = dead_x - x;
    this.velocity_x = target_l / this.lifetime;
    this.begin_speed = this.cur_speed;
    return this;
  }
  private tick: number = 0
  update(dt: number) {
    this.tick += dt
    if (this.tick > 16) {
      this.coords.forward(this.x, this.y)
      this.tick = 0;
    }
    this.velocity_y += this.world.gravity * dt;
    if (this.show_target) {
      this.target_radius = 0.5 * (Math.sin(this.lifetime / 100) + 1) * this.world.max_target_radius;
    }
    this.x += this.velocity_x * dt;
    this.y += this.velocity_y * dt;
    this.lifetime -= dt;
    if (this.lifetime <= 0) {
      this.world.spawn_particles(this.x, this.y, this.hue);
      this.world.fireworks.delete(this);
    }
  };

  draw(ctx: CanvasRenderingContext2D) {
    this.coords.random_curve(ctx, this.x, this.y);
    ctx.globalCompositeOperation = 'lighter';
    ctx.lineWidth = this.line_width;
    ctx.strokeStyle = `hsla(${this.hue}, 100%, ${this.brightness}%, ${this.alpha})`;
    ctx.stroke();

    if (this.show_target) this.draw_target_circle(ctx);
    if (this.world.show_shockwave) this.draw_shockwave(ctx);
  }

  draw_shockwave(ctx: CanvasRenderingContext2D) {
    ctx.globalCompositeOperation = 'source-over';
    const { cur_speed, begin_speed } = this;
    const radius = 10 * (1 - Math.abs(begin_speed - cur_speed) / begin_speed)
    ctx.strokeStyle = `hsla(${this.hue}, 100%, ${this.brightness}%, ${this.alpha})`;
    ctx.lineWidth = radius;
    ctx.save();
    ctx.translate(
      Math.round(this.x),
      Math.round(this.y)
    );
    ctx.rotate(this.cur_angle);
    ctx.beginPath();
    ctx.arc(0, 0, radius, 0, Math.PI, true);
    ctx.strokeStyle = `hsla(${this.hue}, 100%, ${this.brightness}%, ${rand(0.1, 0.3)})`;
    ctx.stroke();
    ctx.restore();
  }

  draw_target_circle(ctx: CanvasRenderingContext2D) {
    ctx.globalCompositeOperation = 'source-over';
    const opacity = (this.world.max_target_radius - this.target_radius) / this.world.max_target_radius
    ctx.strokeStyle = `hsla(${this.hue}, 100%, ${this.brightness}%, ${opacity})`;
    ctx.save();
    ctx.beginPath();
    ctx.arc(
      Math.round(this.dead_x),
      Math.round(this.dead_y),
      this.target_radius,
      0,
      Math.PI * 2,
      false
    );
    ctx.closePath();
    ctx.lineWidth = 1;
    ctx.stroke();
    ctx.restore();

  }
}
